////////////////////////////////////////////////////////////////////////////// 
//
//  cfg_jtag.v
//
//  JTAG配置操作逻辑
//  
//  Original Author: 
//  Current Owner:   
//
////////////////////////////////////////////////////////////////////////////// 
//
// Copyright (C) 2013 Synopsys, Inc.  All rights reserved.
//
// SYNOPSYS CONFIDENTIAL - This is an unpublished, proprietary work of
// Synopsys, Inc., and is fully protected under copyright and trade secret
// laws.  You may not view, use, disclose, copy, or distribute this file or
// any information contained herein except pursuant to a valid written
// license agreement. It may not be used, reproduced, or disclosed to others
// except in accordance with the terms and conditions of that agreement.
//
////////////////////////////////////////////////////////////////////////////// 
//
//    Perforce Information
//    $Author: 
//    $File: /rtl/cfg_jtag.v $
//    $DateTime: 
//    $Revision: 
//
////////////////////////////////////////////////////////////////////////////// 

`include "np_jtag_macros.v"
`include "np_cfg_macros.v"   

`timescale 1ns/10fs
module np_cfg_jtag (
   
// JTAG interface
input  wire                        jtag_rst,
input  wire                        jtag_clk,
input  wire                        jtag_clk_n,
input  wire                        jtag_capture,
input  wire                        jtag_shift,
input  wire                        jtag_update,
input  wire                        jtag_ser_in,
input  wire                        jtag_cfg_sel,
output wire                        jtag_cfg_tdo,

// JTAG -> Internal CFG Interface
//output wire                        cfg_jtag_clk,
output reg  [`NP_CFG_ADDR_RANGE]  cfg_jtag_addr,    //[31:0]
output reg                         cfg_jtag_wr_en,
output reg  [`NP_CFG_DATA_RANGE]  cfg_jtag_wr_data, //[31:0]
output reg                         cfg_jtag_rd_en,
input  wire [`NP_CFG_DATA_RANGE]  cfg_jtag_rd_data,
input  wire                        cfg_jtag_ack
);

reg                                jtag_rd_done;
wire [`NP_JTAG_DR_CFG_LEN-1:0] jtag_capture_val;  //[33:0]
wire [`NP_JTAG_DR_CFG_LEN-1:0] cfg;
wire [`NP_CFG_DATA_RANGE]         cfg_data;
wire                               cfg_cmd_read;
wire                               cfg_cmd_write;
wire                               cfg_cmd_addr;

wire                               cfg_jtag_rd_en_cmd;
wire                               cfg_jtag_wr_en_cmd;

wire                               jtag_update_d;


// 当jtag时钟域中出现ack脉冲时，将读取操作标记为完成。
always @(posedge jtag_clk_n or posedge jtag_rst) begin
  if (jtag_rst)
      jtag_rd_done <= 1'd0;
  else if (jtag_update && (cfg_jtag_rd_en_cmd || cfg_jtag_wr_en_cmd))
      jtag_rd_done <= 1'd0;
  else if (cfg_jtag_ack)
      jtag_rd_done <= 1'd1;            
end

// 对于捕获的数据，把读数据放在低位，把jtag_rd_done放在高位，以便轮询软件知道何时写入了正确的值。
assign jtag_capture_val = {jtag_rd_done, 1'b0, cfg_jtag_rd_data};

// 配置寄存器 - 可通过JTAG访问
// rd_data有多个tck要传到寄存器并进行采样。这里使用两个周期（结果为1+delta tck's）。如果需要，可以增加。
   //
// %%SYNTH:
//   set_multicycle_path -setup 2 -from $cfg_clk -through [get_pins $inst/cfg_reg/capture_val[*]]
//   set_multicycle_path -hold 2 -end -from $cfg_clk -through [get_pins $inst/cfg_reg/capture_val[*]]
//
np_jtag_reg #(.WIDTH(`NP_JTAG_DR_CFG_LEN),
           .RST_VAL(0))
cfg_reg (
  .q           (cfg),
  .serial_out  (jtag_cfg_tdo),
  .select      (jtag_cfg_sel),
  .capture_val (jtag_capture_val),
  .rst         (jtag_rst),
  .clk         (jtag_clk),
  .capture     (jtag_capture),
  .shift       (jtag_shift),
  .serial_in   (jtag_ser_in)
);
    
// 将cfg寄存器分成数据和命令部分
assign cfg_data      =  cfg[`NP_CFG_DATA_RANGE];
assign cfg_cmd_read  = (cfg[`NP_CFG_DATA_LEN+1:`NP_CFG_DATA_LEN] == `NP_CFG_CMD_READ);
assign cfg_cmd_write = (cfg[`NP_CFG_DATA_LEN+1:`NP_CFG_DATA_LEN] == `NP_CFG_CMD_WRITE);
assign cfg_cmd_addr  = (cfg[`NP_CFG_DATA_LEN+1:`NP_CFG_DATA_LEN] == `NP_CFG_CMD_ADDR);


// // CFG clock
// assign cfg_jtag_clk = jtag_clk_n;

// 每当READ或ADDR命令被移入到CFG寄存器中，将用于读/写的地址位捕获到单独的保持寄存器中
//
// Note: falling TCK (cfg) -> rising TCK (cfg_addr) path is real
//
always @(posedge jtag_clk_n or posedge jtag_rst) begin
  if (jtag_rst)
    cfg_jtag_addr <= {`NP_CFG_ADDR_LEN{1'b0}};
  else if (jtag_update && jtag_cfg_sel && (cfg_cmd_read || cfg_cmd_addr))//读出的可以是地址
    cfg_jtag_addr <= cfg_data;
end

// 将用于写访问的写数据位捕获到单独的保持寄存器中
//
// Note: falling TCK (cfg) -> rising TCK (cfg_addr) path is real
always @(posedge jtag_clk_n or posedge jtag_rst) begin
  if (jtag_rst)
    cfg_jtag_wr_data   <= {`NP_CFG_DATA_LEN{1'b0}};
  else begin
    if (jtag_update && jtag_cfg_sel)
      cfg_jtag_wr_data <= cfg_data;
  end
end

// 检测对CFG寄存器的写入，以激活寄存器访问序列。  
// Note: falling TCK (cfg) -> rising TCK (cfg_trig) path
// which is a valid 1/2-cycle path (at low frequency).
//
assign cfg_jtag_rd_en_cmd = jtag_cfg_sel && cfg_cmd_read;

assign cfg_jtag_wr_en_cmd = jtag_cfg_sel && cfg_cmd_write;


// CFG地址译码逻辑需要一个cfg_clk周期来生成cfg_sel，
// 需要将cfg_jtag_rd/wr_en信号延迟到CFGS，
// 因为cfg_inline_reg的一个要求是，由于pcatch块，
// 我们不能在与cfg_sel相同的时钟沿拉高（断言）rd/wr_en。
np_gen_pipe_dly #(.RST_VAL(0), .PIPE_DLY(1))
jtag_update_pipe (
  .q   (jtag_update_d),
  .clk (jtag_clk_n),
  .rst (jtag_rst),
  .d   (jtag_update)
);

always @(posedge jtag_clk_n or posedge jtag_rst) begin
  if (jtag_rst) begin
    cfg_jtag_rd_en <= 1'b0;
    cfg_jtag_wr_en <= 1'b0;
  end
  else begin
    cfg_jtag_rd_en <= jtag_update_d && cfg_jtag_rd_en_cmd;
    cfg_jtag_wr_en <= jtag_update_d && cfg_jtag_wr_en_cmd;
  end
end

endmodule // cfg_jtag
